package org.zw.zcommon.zcredis.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.ZSetOperations;
import org.zw.zcommon.zcdate.ZCDate;
import org.zw.zcommon.zcpage.ZCPageHtml;
import org.zw.zcommon.zcpage.model.ZCPageDataModel;
import org.zw.zcommon.zcpage.model.ZCPageHtmlModel;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Redis操作工具类
 * 提供对Redis的常用操作封装，包括分页查询、批量执行等
 * @author singleton-zw
 * @version 1.0.0
 */
public class ZCRedis {
    private static final Logger logger = LoggerFactory.getLogger(ZCRedis.class);
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 构造函数
     * @param redisTemplate Redis模板
     */
    public ZCRedis(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    
    /**
     * 分页查询ZSet数据
     * @param key 键
     * @param size 每页数量
     * @param page 页码
     * @param startTime 开始时间(yyyy-MM-dd HH:mm:ss)
     * @param endTime 结束时间(yyyy-MM-dd HH:mm:ss)
     * @return 分页数据模型
     */
    public ZCPageDataModel<ZSetOperations.TypedTuple<Object>> pageData(String key, Integer size, Integer page, String startTime, String endTime) {
        long startTimeLong = ZCDate.getTimeStamp(startTime);
        long endTimeLong = ZCDate.getTimeStamp(endTime);
        return pageData(key, size, page, startTimeLong, endTimeLong);
    }

    /**
     * 分页查询ZSet数据
     * @param key 键
     * @param size 每页数量
     * @param page 页码
     * @param startTime 开始时间戳
     * @param endTime 结束时间戳
     * @return 分页数据模型
     */
    public ZCPageDataModel<ZSetOperations.TypedTuple<Object>> pageData(String key, Integer size, Integer page, Long startTime, Long endTime){
        // 参数校验
        if (key == null || key.isEmpty()) {
            logger.warn("Redis key is null or empty");
            return new ZCPageDataModel<>(new ArrayList<>(), 0, page, size);
        }
        
        // 设置默认值
        int pageSize = (size != null && size > 0) ? size : 10;
        int pageNum = (page != null && page > 0) ? page : 1;
        int start = (pageNum - 1) * pageSize;
        
        List<ZSetOperations.TypedTuple<Object>> list = new ArrayList<>();
        long count = 0;
        
        try {
            if (startTime != null && endTime != null) {
                // 按时间范围查询
                count = redisTemplate.opsForZSet().count(key, startTime, endTime);
                Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet()
                        .reverseRangeByScoreWithScores(key, startTime, endTime, start, pageSize);
                list = new ArrayList<>(typedTuples);
            } else {
                // 全量查询
                Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet()
                        .reverseRangeWithScores(key, start, start + pageSize - 1);
                list = new ArrayList<>(typedTuples);
                count = redisTemplate.opsForZSet().zCard(key);
            }
        } catch (Exception e) {
            logger.error("Error occurred while querying Redis ZSet with key: {}", key, e);
            return new ZCPageDataModel<>(new ArrayList<>(), 0, page, size);
        }
        
        return new ZCPageDataModel<>(list, count, pageNum, pageSize);
    }

    /**
     * 分页查询ZSet数据并返回HTML分页信息
     * @param key 键
     * @param size 每页数量
     * @param page 页码
     * @param startTime 开始时间(yyyy-MM-dd HH:mm:ss)
     * @param endTime 结束时间(yyyy-MM-dd HH:mm:ss)
     * @param request HTTP请求对象
     * @return 包含数据和分页HTML的模型
     */
    public ZCPageHtmlModel pageDataHtml(String key, Integer size, Integer page, String startTime, String endTime, HttpServletRequest request) {
        long startTimeLong = ZCDate.getTimeStamp(startTime);
        long endTimeLong = ZCDate.getTimeStamp(endTime);
        ZCPageDataModel<ZSetOperations.TypedTuple<Object>> pageDataModel = pageData(key, size, page, startTimeLong, endTimeLong);
        
        ZCPageHtmlModel pageHtmlModel = new ZCPageHtmlModel();
        pageHtmlModel.setData(pageDataModel.getList());
        pageHtmlModel.setPageHtml(ZCPageHtml.getPageHtml(request, pageDataModel.getTotal(), pageDataModel.getPage(), pageDataModel.getSize()));
        return pageHtmlModel;
    }

    /**
     * 批量执行Redis操作
     * @param keys 键列表
     * @param values 查询字段数组
     * @return 执行结果列表
     */
    public List<Object> executePipelined(List<String> keys, String... values) {
        if (keys == null || keys.isEmpty()) {
            return new ArrayList<>();
        }
        
        if (values == null || values.length == 0) {
            return new ArrayList<>();
        }
        
        try {
            if(values.length>1) {
                return redisTemplate.executePipelined(new SessionCallback<Object>() {
                    @Override
                    public Object execute(RedisOperations operations) {
                        for (String key : keys) {
                            operations.opsForHash().multiGet(key, java.util.Arrays.asList(values));
                        }
                        return null;
                    }
                });
            }
            if(values.length==1){
                return redisTemplate.executePipelined(new SessionCallback<Object>() {
                    public Object execute(RedisOperations operations) {
                        for (String key : keys) {
                            operations.opsForHash().get(key, values[0]);
                        }
                        return null; // 不关心每个命令的返回结果，只关心整体的执行结果。
                    }
                });
            }
        } catch (Exception e) {
            logger.error("Error occurred while executing pipelined operations", e);
            return new ArrayList<>();
        }
        return new ArrayList<>();
    }

    /**
     * 获取ZSet范围数据
     * @param asc 是否升序
     * @param key 键
     * @param start 开始位置
     * @param end 结束位置
     * @return ZSet元素列表
     */
    public List<ZSetOperations.TypedTuple<Object>> zSetList(boolean asc, String key, long start, long end) {
        if (key == null || key.isEmpty()) {
            logger.warn("Redis key is null or empty");
            return new ArrayList<>();
        }
        
        try {
            if (asc) {
                Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet()
                        .rangeByScoreWithScores(key, start, end);
                return new ArrayList<>(typedTuples);
            } else {
                Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet()
                        .reverseRangeByScoreWithScores(key, start, end);
                return new ArrayList<>(typedTuples);
            }
        } catch (Exception e) {
            logger.error("Error occurred while querying ZSet list with key: {}", key, e);
            return new ArrayList<>();
        }
    }

    /**
     * 获取ZSet范围数据数量
     * @param key 键
     * @param start 开始分数
     * @param end 结束分数
     * @return 数据数量
     */
    public int zSetListCount(String key, long start, long end) {
        if (key == null || key.isEmpty()) {
            logger.warn("Redis key is null or empty");
            return 0;
        }
        
        try {
            return redisTemplate.opsForZSet().count(key, start, end).intValue();
        } catch (Exception e) {
            logger.error("Error occurred while counting ZSet with key: {}", key, e);
            return 0;
        }
    }

    /**
     * 分页获取ZSet范围数据
     * @param asc 是否升序
     * @param key 键
     * @param start 开始分数
     * @param end 结束分数
     * @param offset 偏移量
     * @param count 数量
     * @return ZSet元素列表
     */
    public List<ZSetOperations.TypedTuple<Object>> zSetListPage(boolean asc, String key, long start, long end, long offset, long count) {
        if (key == null || key.isEmpty()) {
            logger.warn("Redis key is null or empty");
            return new ArrayList<>();
        }
        
        try {
            if (asc) {
                Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet()
                        .rangeByScoreWithScores(key, start, end, offset, count);
                return new ArrayList<>(typedTuples);
            } else {
                Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet()
                        .reverseRangeByScoreWithScores(key, start, end, offset, count);
                return new ArrayList<>(typedTuples);
            }
        } catch (Exception e) {
            logger.error("Error occurred while querying ZSet page with key: {}", key, e);
            return new ArrayList<>();
        }
    }
}
